#!/user/bin/env python
# -*- coding: utf-8 -*-
"""
Author: Sjy
Description:
"""

__all__ = []

import os
import re
import copy
import json
from typing import Dict, Any

import gui.controls.envs as envs
from .base import BaseModule
from ..pops import Pops
from ..uis import ImportAnalysisModel, LoadCalculations
from ..actions import Actions
from ..project import project_manager
from ..params_model import (
    DlgBaseInfo,
    DlgLifeAnalysis,
    DlgMaterialLibrary,
    DlgParamsModeling,
)

import module.fatigue_life_analysis as fatigue_life_analysis

OUTPUT_CONFIG_KEY = "_OUTPUT_CONFIG"


class ExtremeLoadModule(BaseModule):
    """极限受载分析模块"""

    def __init__(self):
        super().__init__()
        self._draw_line_item = []
        self._draw_line_items = []
        self._current_load_dlg = None

    def init_category(self, ribbon: envs.Ribbonbar) -> Any:
        """初始化工具栏项
        Args:
            ribbon: 工具栏
        Returns:
            Any: 工具栏项
        """
        categoryExtremeLoadCapacity = ribbon.add_category("极限受载分析模块")
        # 导入模型→分析设置→载荷计算
        pannelExtremeLoadCapacity = ribbon.add_panel(
            "工作流", categoryExtremeLoadCapacity, titleHide=True
        )

        pannelParamControl = ribbon.add_panel(
            "参数控制", categoryExtremeLoadCapacity, titleHide=True
        )

        ribbon.add_large_btn(
            pannelExtremeLoadCapacity,
            {
                "title": "导入模型",
                "style": "large",
                "styleSheet": "margin-right:10px; padding: 10px 20px;",
                "action": lambda *args: self.import_model(),
            },
        )
        ribbon.add_large_btn(
            pannelExtremeLoadCapacity,
            {
                "title": "分析设置",
                "style": "large",
                "styleSheet": "margin-right:10px; padding: 10px 20px;",
                "action": lambda *args: self.analysis(),
            },
        )
        ribbon.add_large_btn(
            pannelExtremeLoadCapacity,
            {
                "title": "载荷计算",
                "style": "large",
                "styleSheet": "margin-right:10px; padding: 10px 20px;",
                "action": lambda *args: self.show_result(),
            },
        )

        ribbon.add_large_btn(
            pannelParamControl,
            {
                "title": "设置参数文件",
                "style": "large",
                "styleSheet": "margin-right:10px; padding: 10px 20px;",
                "action": lambda *args: self.set_output_path(),
            },
        )
        ribbon.add_large_btn(
            pannelParamControl,
            {
                "title": "生成参数数据",
                "style": "large",
                "styleSheet": "margin-right:10px; padding: 10px 20px;",
                "action": lambda *args: self.generate_data_file(),
            },
        )

        return categoryExtremeLoadCapacity

    def init_dock(self, tree) -> None:
        data = [
            {
                "title": "极限受载分析",
                "children": [
                    {
                        "title": "导入模型",
                        "events": {"dbClick": lambda params: self.import_model()},
                    },
                    {
                        "title": "分析设置",
                        "events": {"dbClick": lambda params: self.analysis()},
                    },
                    {
                        "title": "载荷计算",
                        "events": {"dbClick": lambda params: self.show_result()},
                    },
                ],
            }
        ]

        tree._set_data(data)

    def import_model(self):
        """导入模型"""
        py3d = envs.GL.get_value("py3d")
        dlg = ImportAnalysisModel(parent=py3d)
        data = project_manager.get_extreme_load_data() or {}
        dlg.setWindowTitle("导入分析模型")
        if dlg:
            curData = {
                "mainModelPath": data.get("mainModelPath", ""),
                "subModelPath": data.get("subModelPath", ""),
            }

            dlg.init(curData)
            dlg.sigOnSave.connect(lambda data: self.save_model_data(data))
            dlg.sigOnRunParamsModeling.connect(
                lambda data: self.run_params_modeling(data)
            )

    def save_model_data(self, data: dict = {}):
        """保存模型数据"""
        project_manager.update_extreme_load_data(
            {
                "mainModelPath": data.get("mainModelPath", ""),
                "subModelPath": data.get("subModelPath", ""),
            }
        )
        project_manager.save_extreme_load_data()
        # 确认后要弹出基本信息界面
        self.show_basic_info_dlg()

    def run_params_modeling(self, data: dict = {}):
        py3d = envs.GL.get_value("py3d")

        data = project_manager.get_extreme_load_data() or {}
        dbParamsModeling = data.get("paramsModeling", {})
        dlgParamsModeling = DlgParamsModeling.show(py3d)
        if dlgParamsModeling:
            dlgParamsModeling.init(dbParamsModeling)
            dlgParamsModeling.resize(1000, 1000)
            dlgParamsModeling.paramsModeling_save_signal.connect(
                lambda data: self.paramsModeling_save_signal(data)
            )

    def paramsModeling_save_signal(self, data: dict = {}):
        """保存参数建模数据
        2025年5月30日
        要求1:
            删除“上铺层修补层的’铺层角度’”与“蜂窝芯子修补层"
        要求2:
            "下铺层修补层”与“下铺层填充层”在前两种损伤时，应处于无效状态
        """
        # project_manager.update_extreme_load_data({"paramsModeling": data})
        # project_manager.save_extreme_load_data()

        delKeys = []
        if data.get("损伤类型") != "贯穿式损伤":
            delKeys = [
                "额外挖除尺寸",
                "下铺层修补胶层层数",
                "下铺层修补层层数",
                "下铺层修补层",
                "下铺层填充层",
            ]
        if not (
            data.get("损伤类型") == "蜂窝芯子损伤"
            or data.get("损伤类型") == "贯穿式损伤"
        ):
            delKeys.append("蜂窝芯子修补层")

        db = project_manager.get_extreme_load_data() or {}
        dbParamsModeling = db.get("paramsModeling", {})

        db["paramsModeling"] = envs.utils.recursion_update_data(
            dbParamsModeling, data, delKeys=delKeys
        )
        project_manager.set_extreme_load_data(db)
        project_manager.save_extreme_load_data()

    def show_basic_info_dlg(
        self, data: dict = {}, failureAreas: list = [], failureTypes: list = []
    ):
        """显示基本信息界面

        Args:
            data (dict, optional): 分析模型数据. Defaults to {}.
            failureAreas (list, optional): 失效区域. Defaults to [].
            failureTypes (list, optional): 失效类型. Defaults to [].
        """
        py3d = envs.GL.get_value("py3d")
        dlgBaseInfo = DlgBaseInfo.show(py3d)
        failureAreas = Actions.get_cae_failure_areas()
        data = project_manager.get_extreme_load_data() or {}
        failureTypes = [
            {"title": "金属材料破坏", "value": "金属材料破坏"},
            {"title": "金属材料低周疲劳", "value": "金属材料低周疲劳"},
            {"title": "金属材料高周疲劳", "value": "金属材料高周疲劳"},
            {"title": "复合材料破坏", "value": "复合材料破坏"},
        ]
        if dlgBaseInfo:
            curData = {
                "analysisArea": data.get("analysisArea"),
                "failureTypes": data.get("failureTypes"),
            }
            dlgBaseInfo.baseInfo_save_signal.connect(
                lambda data: self.save_basic_info_data(data)
            )
            dlgBaseInfo.init(
                data=curData, analysisAreaOpt=failureAreas, failureTypesOpt=failureTypes
            )

    def save_basic_info_data(self, data: dict = {}):
        """保存基本信息数据"""
        project_manager.update_extreme_load_data(data)
        project_manager.save_extreme_load_data()

    def analysis(self):
        """分析设置"""
        data = project_manager.get_repair_scheme_strength_data() or {}
        failureTypes = data.get("failureTypes")

        if failureTypes:
            pass

        #     if failureTypes == "金属材料破坏":
        #         self.analysis_metal_material_failure()
        #     elif failureTypes == "金属材料低周疲劳":
        #         self.analysis_metal_material_low_cycle_fatigue()
        #     elif failureTypes == "金属材料高周疲劳":
        #         pass
        #     elif failureTypes == "复合材料破坏":
        #         pass

        # 金属材料低周疲劳  当前仅有金属材料低周疲劳
        py3d = envs.GL.get_value("py3d")
        dlg = DlgLifeAnalysis.show(py3d)

        data = project_manager.get_extreme_load_data() or {}
        dbLifeAnalysis = data.get("lifeAnalysis", {})
        if dlg:
            dlg.init(dbLifeAnalysis)
            dlg.lifeAnalysis_save_signal.connect(
                lambda data: self.lifeAnalysis_save_signal(data)
            )
            dlg.lifeAnalysis_calculate_signal.connect(
                lambda data, dlg=dlg: self.cal_result(data, dlg)
            )

        cfgIndex = {}
        with open("config/sys/index.json", "r", encoding="utf-8") as file:
            cfgIndex = json.load(file) or {}
        theme = cfgIndex.get("theme", "dark")  # dark or light
        qss = "styles/dlg.qss"
        if theme == "light":
            qss = "styles/dlg_light.qss"

        dlg.setStyleSheet(open(qss, "r").read())

    def cal_result(self, data: dict = {}, dlg: DlgLifeAnalysis = None):
        """
        疲劳寿命分析函数
        需要用到numpy库

        参数:
            Y: array-like, 一列数据（即维修数据）
            Sm: float, 形状参数
            St: float, 试件系数
            Sc: float, 置信系数
            Sr: float, 可靠性系数

        返回:
            N95: float, 95%置信度下的疲劳寿命
            belta: float, 计算得到的贝塔值
        """
        Y = data.get("Y", [])
        Sm = data.get("Sm", 0)
        St = data.get("St", 0)
        Sc = data.get("Sc", 0)
        Sr = data.get("Sr", 0)

        # print(Y, Sm, St, Sc, Sr)
        try:
            result = fatigue_life_analysis.fatigue_life_analysis(Y, Sm, St, Sc, Sr)
            # print("result = ", result)
            dlg.updateCalculateRes(str(result))
            return result
        except Exception as e:
            envs.MBox.warning(f"计算异常: {e}", parent=envs.GL.get_value("py3d"))
            return

    def lifeAnalysis_save_signal(self, data: dict = {}):
        """保存数据"""
        project_manager.update_extreme_load_data({"lifeAnalysis": data})
        project_manager.save_extreme_load_data()

        # self.lower_save_signal("寿命分析", data)

        data = project_manager.get_extreme_load_data() or {}
        dbMaterialLibrary = data.get("materialLibrary", {})

        try:
            # 保存完的数据要 弹出 材料属性界面
            py3d = envs.GL.get_value("py3d")
            dlg = DlgMaterialLibrary.show(py3d)
            if dlg:
                if hasattr(dlg, "init"):
                    dlg.init(dbMaterialLibrary)
                dlg.materialLibrary_save_signal.connect(
                    lambda data: self.materialLibrary_save_signal(data)
                )
        except Exception as e:
            envs.MBox.warning(
                f"材料属性界面异常: {e}", parent=envs.GL.get_value("py3d")
            )

    # def lower_save_signal(self, key, data:dict={}):
    #     """低周疲劳 保存数据"""
    #     path = "data/ipimas"
    #     if not os.path.exists(path):
    #         os.makedirs(path)
    #     file = f"{path}/input_lower.json"
    #     oldData = Actions.get_data(file) or {}
    #     oldData[key]= data
    #     Actions.save_data(oldData, file, update=False)
    def materialLibrary_save_signal(self, data: dict = {}):
        """材料属性 保存数据"""
        project_manager.update_extreme_load_data({"materialLibrary": data})
        project_manager.save_extreme_load_data()

    def show_result(self):
        """显示结果"""
        py3d = envs.GL.get_value("py3d")

        db = project_manager.get_extreme_load_data()

        dbLifeAnalysis = db.get("lifeAnalysis", {})
        loadCalculation = db.get("loadCalculation", {})

        optimizationAlgorithmsList = [{"title": "二分法", "value": "二分法"}]
        echarts_data = {
            "xFontLabel": "迭代次数",
            "yFontLabel": "目标距离值",
            "x": [],
            "y": [],
        }
        # print("dbLifeAnalysis = ", dbLifeAnalysis.get("寿命统计结果", ""))
        dlg = LoadCalculations(
            {
                **loadCalculation,
                "lifeStatisticsResults": dbLifeAnalysis.get("寿命统计结果", ""),
            },
            optimizationAlgorithmsList,
            echarts_data,
            parent=py3d,
        )
        self._current_load_dlg = dlg
        if dlg:
            # save_data_signal=pyqtSignal(dict)
            # start_operation=pyqtSignal(dict)
            # show_operation=pyqtSignal(dict)
            dlg.save_data_signal.connect(
                lambda data: self.loadCalculation_save_signal(data)
            )  #  这个是保存数据并退出
            dlg.start_operation.connect(
                lambda data, dlg=dlg: self.start_operation(data)
            )
            dlg.show_operation.connect(lambda data: self.show_operation(data))
            dlg.use_data_signal.connect(
                lambda data: self.use_data_signal(data)
            )  # 这个是应用
            dlg.show()

    def loadCalculation_save_signal(self, data: dict = {}):
        """载荷计算 保存数据"""
        project_manager.update_extreme_load_data({"loadCalculation": data})
        project_manager.save_extreme_load_data()
        self.tip_to_file()

    def use_data_signal(self, data: dict = {}):
        """应用数据"""
        project_manager.update_extreme_load_data({"loadCalculation": data})
        project_manager.save_extreme_load_data()
        self.tip_to_file()

    def tip_to_file(self):
        """提示文件"""

        db = project_manager.get_extreme_load_data() or {}
        dbOutput = db.get(OUTPUT_CONFIG_KEY, {})
        output_file = dbOutput.get("output_file")
        if not output_file:
            return
        isConfirm = envs.MBox.confirm(
            "是否立即更新参数文件？", parent=envs.GL.get_value("py3d")
        )
        if isConfirm:
            self.generate_data_file()

    def start_operation(self, data: dict = {}):
        """运算按钮 执行matlab 程序 TODO: 执行matlab 程序"""
        # print("开始操作", data)
        envs.ipimas_toLog("开始运算:")
        self._current_load_dlg.update_plot(None, update_inversionResults="")
        envs.apis.pexecute("A1.exe", shell=True, isEmit=True, cwd="module/find_load/")

    def reg_exe_tips(self):
        """注册 exe 运行获取相关提示信息"""
        envs.apis.pexecute_emit(lambda msg: self.exe_tips(msg))

    def exe_tips(self, msg: str = ""):
        """exe 运行获取相关提示信息"""
        # print("exe 运行获取相关提示信息", msg)
        if not self._current_load_dlg:
            return
        if "number of iterations:" in msg:
            # 解析 msg 的字符串获取 number of iteration 的值 注意不确定格式仅能确定后面是数字
            number_of_iteration = int(re.search(r"\d+\.?\d*", msg).group())
            self._draw_line_item.append(number_of_iteration)
            envs.ipimas_toLog(f"  {msg}")
        elif "amplitude:" in msg:
            number_of_iteration = float(re.search(r"\d+\.?\d*", msg).group())
            self._draw_line_item.append(number_of_iteration)
            envs.ipimas_toLog(f"  {msg}")
        elif "Over,The load amplitude is:" in msg:
            envs.ipimas_toLog(f"{msg}")
            envs.ipimas_toLog(f"\n")
            amplitude = float(re.search(r"\d+\.?\d*", msg).group())
            self._current_load_dlg.update_plot(
                None, update_inversionResults=f"{amplitude}"
            )
            self._draw_line_items = []  # 清空
        if len(self._draw_line_item) == 2:
            envs.ipimas_toLog(f"\n")
            self._draw_line_items.append(self._draw_line_item)
            if hasattr(self._current_load_dlg, "update_plot"):
                echarts_data = {
                    "x": [item[0] for item in self._draw_line_items],
                    "y": [item[1] for item in self._draw_line_items],
                    "xFontLabel": "迭代次数",
                    "yFontLabel": "载荷幅值",
                }
                self._current_load_dlg.update_plot(echarts_data)
            self._draw_line_item = []

    def show_operation(self, data: dict = {}):
        """显示按钮 TODO:显示折线图"""
        # print("显示操作", data)

    def set_output_path(self):
        """设置输出路径"""
        db = project_manager.get_repair_scheme_data() or {}
        dbOutput = db.get(OUTPUT_CONFIG_KEY, {})
        output_file = dbOutput.get("output_file", "")

        saveFileInfo = envs.UFile.save_file(
            "json", file=output_file, parent=envs.GL.get_value("py3d")
        )
        if not saveFileInfo or not saveFileInfo.get("file"):
            return
        file = saveFileInfo.get("file")
        # 检查路径是否存在
        dbOutput["output_file"] = file
        project_manager.update_extreme_load_data({OUTPUT_CONFIG_KEY: dbOutput})
        project_manager.save_extreme_load_data()
        envs.MBox.success(
            "已成功设置输出参数文件的位置", parent=envs.GL.get_value("py3d")
        )

    def generate_data_file(self):
        """生成数据文件"""
        # 获取输出的路径地址
        db = project_manager.get_extreme_load_data() or {}
        dbOutput = db.get(OUTPUT_CONFIG_KEY, {})
        output_file = dbOutput.get("output_file")
        if not output_file:
            return envs.MBox.warning(
                "请先设置输出参数文件的位置", parent=envs.GL.get_value("py3d")
            )

        if not os.path.exists(os.path.dirname(output_file)):
            return envs.MBox.warning(
                "输出文件的路径不存在,请重新设置输出参数文件的位置",
                parent=envs.GL.get_value("py3d"),
            )

        # 获取数据库数据
        dbDataBase = project_manager.get_database_data() or {}
        # 获取参数化建模数据
        materials = []
        materialNameMaps = {}
        materialMaps = {
            "材料名称": "Name of material",
            "材料类型": "Type of material",
            "密度": "Density",
        }

        for key, value in dbDataBase.items():
            for row in value:
                material = copy.deepcopy(row)
                for key, value in materialMaps.items():
                    if (
                        key in material
                    ):  # 如果key在row中 则将row[key]的值赋值给material[value]
                        material[value] = row.get(key)
                        del material[key]
                materials.append(material)
                materialNameMaps[material.get("Name of material")] = material

        dbLifeAnalysis = db.get("lifeAnalysis", {})
        dbLoadCalculation = db.get("loadCalculation", {})

        outputInputData = {
            "mainModelPath": db.get("mainModelPath", None),  # 主模型路径
            "subModelPath": db.get("subModelPath", None),  # 子模型路径
            "analysisArea": db.get("analysisArea", None),  # 分析区域
            "failureTypes": db.get("failureTypes", None),  # 失效类型
            "lifeAnalysis": {  # 寿命分析
                # "modelFile":dbLifeAnalysis.get("预设模型路径", None), # 预设模型路径 这个应该不需要
                "Shape_Parameter": dbLifeAnalysis.get("形状参数", None),  # 形状参数
                "Specimen_Coefficient": dbLifeAnalysis.get(
                    "试件系数", None
                ),  # 分析区域
                "Confidence_Coefficient": dbLifeAnalysis.get(
                    "置信系数", None
                ),  # 置信系数
                "Reliability_Coefficient": dbLifeAnalysis.get(
                    "可靠性系数", None
                ),  # 可靠性系数
                "Life_Statistical_Result": dbLifeAnalysis.get(
                    "寿命统计结果", None
                ),  # 寿命统计情况
            },
            "materialLibrary": [
                {
                    "Name of material": item.get("材料名称"),  # 目前没用到
                    "Poisson_ratio": item.get("泊松比"),
                    "Fatigue_strength_coefficient": item.get("疲劳强度系数"),
                    "Fatigue_ductility_coefficient": item.get("疲劳延性系数"),
                    "Elastic_modulus": item.get("弹性模量"),
                    "Fatigue_strength_exponent": item.get("疲劳强度指数"),
                    "Fatigue_ductility_exponent": item.get("疲劳延性指数"),
                    "Cyclic_strength_coefficient": item.get("循环强度系数"),
                    "Cyclic_strain_hardening_exponent": item.get("循环应变硬化指数"),
                }
                for item in db.get("materialLibrary", [])
            ],
            "loadCalculation": {  # 载荷计算
                "Optimization_algorithm": self.init_optimization_algorithm_output_value(
                    dbLoadCalculation.get("optimizationAlgorithms")
                ),  # 优化算法
                "Optimization_lower_bound": dbLoadCalculation.get(
                    "optimizeTheNether", None
                ),  # 优化下界
                "Optimization_upper_bound": dbLoadCalculation.get(
                    "optimizeTheTpper", None
                ),  # 优化上界
                "Termination_error": dbLoadCalculation.get(
                    "terminationError", None
                ),  # 终止误差
                "Maximum_iteration_number": dbLoadCalculation.get(
                    "maxCirculate", None
                ),  # 最大迭代次数
            },
            "materials": materials,
        }

        envs.UFile.save_json(output_file, outputInputData, removeNone=True)
        envs.MBox.success("已成功保存文件！", parent=envs.GL.get_value("py3d"))

    def init_optimization_algorithm_output_value(self, value: str = "二分法"):
        """初始化优化算法输出值"""
        if value == "二分法":
            return "Bisection_method"
        return value
